
htab_t js_sym_table = NULL;

/* Symbol table (htab) call back function for entry comparison. This is 
 * just a simple string comparison on the declaration name
 */
static int js_htab_eq (const void *lhs, const void *rhs)
{
  const tree lhs_name = ((const js_symbol_t*)lhs)->name;
  const tree rhs_name = ((const js_symbol_t*)rhs)->name;

  return !strcmp(IDENTIFIER_POINTER(lhs_name),
                 IDENTIFIER_POINTER(rhs_name));
}

/* Symbol table (htab) call back function for entry cleanup */
static void js_htab_del (void *item ATTRIBUTE_UNUSED)
{
  return;
}

/* Symbol table (htab) call back function for traversal purpose.
 * entry is a pointer to the slot containing the real element, though
 * the expression (tree)*entry is the VAR_DECL tree node. arg is the
 * pointer to the global_decl from calc_getdecls
 */
static int js_htab_trav (void **entry, void *arg)
{
  tree *first = (tree*)arg;
  js_symbol_t *sym = *(js_symbol_t**)entry;
  TREE_CHAIN(sym->decl) = *first;
  *first = sym->decl;

  return true;
}

/* Symbol table (htab) call back function for hash value generation */
static hashval_t js_htab_hash (const void *item)
{
  const tree name = ((const js_symbol_t*)item)->name;
  const char *decl_name = IDENTIFIER_POINTER(name);
  size_t i, len = strlen(decl_name);
  hashval_t h = 0;
  for (i = 0; i < len; i++) {
      h = decl_name[i] + 31 * h;
  }
  return h;
}

/* Symbol table initializaton */
int js_init_tables (void)
{
  js_sym_table = htab_create(128, js_htab_hash, js_htab_eq, js_htab_del);
  return 0;
}

/* Symbol table finalization */
int js_del_tables (void)
{
  htab_delete (js_sym_table);
  return 0;
}

typedef tree (*fsymbol)(tree expr);

static js_symbol_t *new_jssymbol(tree name, tree decl)
{
  js_symbol_t *sym = XNEW(js_symbol_t);
  sym->name = name;
  sym->decl = decl;
  return sym;
}

static tree __push_decl_to_symtable(tree name, tree decl)
{
  PTR *slot;
  js_symbol_t *sym;
  sym  = new_jssymbol(name, decl);
  slot = htab_find_slot(js_sym_table, sym, INSERT);
  *slot = sym;
  js_append_to_current_decls(decl, js__block_level);
  return decl;
}

tree push_decl_to_symtable(tree name, tree expr)
{
  return __push_decl_to_symtable(name, expr);
}


static tree push_to_symtable(tree expr)
{
  tree decl;
  decl = build_decl(CURRENT_LOCATION(), VAR_DECL, expr, object_type_ptr);
  return __push_decl_to_symtable(expr, decl);
}

static tree symbol_not_found(tree expr)
{
  const char *name = IDENTIFIER_POINTER(expr);
  error("line %d: variable %s isnot yet defined", CURRENT_LOCATION(), name);
  exit(FATAL_EXIT_CODE);
  return error_mark_node;
}

static int id_decl_eq(tree id, tree decl)
{
  const char *id_name   = IDENTIFIER_POINTER(id);
  const char *decl_name = IDENTIFIER_POINTER(decl);
  return !strcmp(id_name, decl_name);
}

static int id_str_eq(tree id, const char *decl_name)
{
  const char *id_name   = IDENTIFIER_POINTER(id);
  return !strcmp(id_name, decl_name);
}

static tree find_symbol_args(tree expr, fsymbol f)
{
  int i, j, level = js__block_level;
  for (i = level; i >= 0; --i) {
      js_symbol x;
      VEC(js_symbol,gc) *args = get_level_args(i);
      for (j = 0; VEC_iterate(js_symbol, args, j, x); ++j) {
          if (id_decl_eq(x->name, expr)) {
              return x->decl;
          }
      }
  }
  return f(expr);
}

static tree find_symbol(tree expr, fsymbol f)
{
  js_symbol_t sym, *res;
  const char *fname = NULL;
  char buf[128];
  sym.name = expr;
  sym.decl = (tree) 0xdeadbeaf;
  if ((res = (js_symbol)htab_find(js_sym_table, &sym)) == HTAB_EMPTY_ENTRY) {
      if (TREE_CODE(expr) == IDENTIFIER_NODE) {
          sprintf(buf, "__%s", IDENTIFIER_POINTER(expr));
          fname = buf;
      }
      if (fname) {
          sym.name = js_build_id(fname);
          if ((res = (js_symbol)htab_find(js_sym_table, &sym)) != HTAB_EMPTY_ENTRY) {
              return res->decl;
          }
      }
      return find_symbol_args(expr, f);
  }
  else
      return res->decl;
}

